# detailed_comparer.py
# -*- coding: utf-8 -*-

import os
import logging
from typing import Dict, Any, List, Tuple, Set, Optional

# 导入项目模块
import config
from plagiarism_core.core_engine import ProcessedDocData
import plagiarism_core.similarity_calculator as similarity_calculator
from plagiarism_core.semantic_comparer import SemanticComparer, SemanticMatchWithPosTuple
from plagiarism_core.similarity_calculator import MatchTuple

# 获取日志记录器
logger = logging.getLogger(__name__)

class DetailedComparer:
    """
    执行详细相似度比较的类。
    在 CandidateGenerator 筛选出的候选文档集上运行具体的比较算法。
    """

    def __init__(self,
                 corpus_data: Dict[str, ProcessedDocData],
                 semantic_comparer_instance: Optional[SemanticComparer] = None):
        """
        初始化详细比较器。
        """
        logger.info("初始化 DetailedComparer...")
        self.corpus_data = corpus_data
        self.semantic_comparer = semantic_comparer_instance

        if config.USE_SEMANTIC_SIMILARITY and not self.semantic_comparer:
             logger.warning("配置要求进行语义比较，但未提供有效的 SemanticComparer 实例。语义比较将被跳过。")
        elif not config.USE_SEMANTIC_SIMILARITY and self.semantic_comparer:
             logger.info("配置中禁用了语义相似度计算，提供的 SemanticComparer 实例将不会被用于比较。")
             # self.semantic_comparer = None # 可以选择在此处禁用

        logger.info("DetailedComparer 初始化完成。")


    def compare(self,
                input_data: ProcessedDocData,
                candidate_ids: Set[str]
                ) -> Dict[str, Any]:
        """
        对输入文档和候选语料库文档执行详细的相似度比较。
        """
        task_id_str = f"[任务 {getattr(input_data, 'task_id', '未知')}]" # 尝试获取任务ID
        input_filename = os.path.basename(input_data['source_file_path'])
        logger.info(f"{task_id_str} 开始对输入文档 '{input_filename}' 与 {len(candidate_ids)} 个候选文档进行详细比较...")

        # 初始化结果字典
        comparison_results: Dict[str, Any] = {
            'literal_matches': [],
            'semantic_matches': [],
            'ngram_scores': {}
        }

        # 如果候选集为空，则无需比较，直接返回空结果
        if not candidate_ids:
            logger.info(f"{task_id_str} 候选集为空，跳过详细比较。")
            return comparison_results

        # --- 准备比较所需的数据 ---
        # 1. 获取输入文本
        cleaned_input_text = input_data.get("cleaned_text")
        if not cleaned_input_text:
             logger.error(f"{task_id_str} 输入文档 '{input_filename}' 的清理后文本为空，无法进行详细比较！")
             return comparison_results # 返回空结果

        # 2. 获取候选语料库的文本内容
        corpus_texts_to_compare: Dict[str, str] = {}
        valid_candidate_ids = set() # 存储实际有效的候选 ID
        for cid in candidate_ids:
            if cid in self.corpus_data and self.corpus_data[cid].get("cleaned_text"):
                corpus_texts_to_compare[cid] = self.corpus_data[cid]["cleaned_text"]
                valid_candidate_ids.add(cid)
            else:
                logger.warning(f"{task_id_str} 候选文档 ID '{cid}' 在语料库数据中不存在或文本为空，将从详细比较中排除。")

        if not corpus_texts_to_compare:
            logger.info(f"{task_id_str} 没有有效的候选语料库文本可供比较（可能所有候选都为空或不存在），跳过详细比较。")
            return comparison_results
        logger.info(f"{task_id_str} 将对 {len(corpus_texts_to_compare)} 个有效的候选文档进行详细比较。")


        # --- 执行各种比较 ---

        # 1. 字面匹配 (SequenceMatcher) 和 N-gram 相似度
        # 这两者通常可以一起计算，因为它们都基于文本内容
        logger.info(f"{task_id_str} 开始计算字面匹配和 N-gram 相似度...")
        try:
            # 调用 similarity_calculator 中的函数
            # 它会返回 'matches' (字面) 和 'ngram_scores'
            similarity_results = similarity_calculator.calculate_similarity(
                cleaned_input_text, corpus_texts_to_compare
            )
            # 存储结果
            comparison_results['literal_matches'] = similarity_results.get('matches', [])
            comparison_results['ngram_scores'] = similarity_results.get('ngram_scores', {})
            logger.info(f"{task_id_str} 字面匹配找到 {len(comparison_results['literal_matches'])} 个片段。")
            if config.USE_NGRAM_SIMILARITY:
                 logger.info(f"{task_id_str} N-gram 相似度计算完成，覆盖 {len(comparison_results['ngram_scores'])} 个文档。")
        except Exception as e:
             logger.exception(f"{task_id_str} 计算字面匹配或 N-gram 相似度时发生错误: {e}")
             # 即使出错，也继续尝试其他比较


        # 2. 语义相似度计算
        if config.USE_SEMANTIC_SIMILARITY:
            if self.semantic_comparer and self.semantic_comparer.enabled:
                logger.info(f"{task_id_str} 开始计算语义相似度...")
                # 准备输入块 (根据配置选择句子或段落)
                chunk_type = config.SEMANTIC_CHUNK_TYPE
                input_chunks_with_pos = input_data.get("sentences_pos") if chunk_type == 'sentence' else input_data.get("paragraphs_pos", [])

                if not input_chunks_with_pos:
                     logger.warning(f"{task_id_str} 未找到用于语义比较的输入块 (类型: {chunk_type})，跳过语义相似度计算。")
                else:
                    try:
                        # 调用 SemanticComparer 的方法进行比较
                        # 注意：传递的是有效的候选语料文本字典 corpus_texts_to_compare
                        semantic_matches = self.semantic_comparer.find_similar_chunks(
                            input_chunks_with_pos,
                            corpus_texts_to_compare # 只比较候选集
                        )
                        comparison_results['semantic_matches'] = semantic_matches
                        logger.info(f"{task_id_str} 语义相似度计算完成，找到 {len(semantic_matches)} 个高于阈值的匹配块。")
                    except Exception as e:
                         logger.exception(f"{task_id_str} 计算语义相似度时发生错误: {e}")
            else:
                 logger.warning(f"{task_id_str} 配置要求进行语义比较，但 SemanticComparer 不可用或未启用，跳过此步骤。")
        else:
             logger.debug(f"{task_id_str} 配置中禁用语义相似度计算。")

        logger.info(f"{task_id_str} 详细相似度比较阶段完成。")
        return comparison_results
